﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Sakura.Media.AviSynth
{
	/// <summary>
	/// 表示一个 AVS 执行环境。无法继承该类型。
	/// </summary>
	public sealed class AvsEnvironment
	{
		/// <summary>
		/// 环境的内部指针。
		/// </summary>
		internal IntPtr ptr;

		/// <summary>
		/// 创建一个新的执行环境。
		/// </summary>
		/// <param name="ptr">新环境的内部指针。</param>
		private AvsEnvironment(IntPtr ptr)
		{
			this.ptr = ptr;
		}

		/// <summary>
		/// 创建一个指定版本的 AVS 环境。
		/// </summary>
		/// <param name="version">要创建的 AVS 环境的版本。</param>
		/// <returns>指定版本的执行环境。如果创建失败，则返回 <c>null</c>。</returns>
		public static AvsEnvironment Create(int version)
		{
			var ptr = Native.NativeMethods.avs_create_script_environment(version);

			if (ptr == IntPtr.Zero)
			{
				return null;
			}
			else
			{
				return new AvsEnvironment(ptr);
			}
		}

		/// <summary>
		/// 析构方法。
		/// </summary>
		~AvsEnvironment()
		{
			Native.NativeMethods.avs_delete_script_environment(this.ptr);
			this.ptr = IntPtr.Zero;
		}

		#region 辅助方法

		/// <summary>
		/// 检查当前的执行环境是否和指定版本兼容。
		/// </summary>
		/// <param name="version">要检查的版本。</param>
		/// <returns>如果当前的执行环境和指定版本兼容，返回 <c>true</c>；否则返回 <c>false</c>。</returns>
		public bool CheckVersion(int version)
		{
			return Native.NativeMethods.avs_check_version(this.ptr, version);
		}

		/// <summary>
		/// 设置环境的工作目录。
		/// </summary>
		/// <param name="newDir">要设置的新工作目录。</param>
		public void SetWorkingDirectory(string newDir)
		{
			if (newDir == null)
			{
				throw new ArgumentNullException("newDir");
			}

			Marshal.ThrowExceptionForHR(Native.NativeMethods.avs_set_working_dir(this.ptr, newDir));
		}

		/// <summary>
		/// 设置环境允许使用的最大内存值。
		/// </summary>
		/// <param name="memorySize">允许使用的最大内存值，以字节为单位。</param>
		public void SetMaximumMemoryUsage(int memorySize)
		{
			Marshal.ThrowExceptionForHR(Native.NativeMethods.avs_set_memory_max(this.ptr, memorySize));
		}

		/// <summary>
		/// 获取该环境下的处理器标志。
		/// </summary>
		public CpuFlags CpuFlags
		{
			get
			{
				return Native.NativeMethods.avs_get_cpu_flags(this.ptr);
			}
		}

		#region 上下文管理

		/// <summary>
		/// 保存当前的上下文。
		/// </summary>
		/// <param name="level">要保存的级别数。</param>
		public void PushContext(int level)
		{
			Native.NativeMethods.avs_push_context(this.ptr, level);
		}

		/// <summary>
		/// 保存当前的上下文。如果需要自动恢复上下文，建议使用 <see cref="UsingContext"/> 结合 using 表达式。
		/// </summary>
		public void PushContext()
		{
			this.PushContext(0);
		}

		/// <summary>
		/// 恢复当前的上下文。
		/// </summary>
		public void PopContext()
		{
			Native.NativeMethods.avs_pop_context(this.ptr);
		}

		/// <summary>
		/// 保存上下文并提供一个可处置的环境凭据。当退出代码块后，自动恢复原始上下文。
		/// </summary>
		/// <returns>可处置的上下文凭据。</returns>
		public IDisposable UsingContext()
		{
			return new ContextToken(this);
		}

		/// <summary>
		/// 用于进行上下文自动切换的辅助凭据对象。
		/// </summary>
		private class ContextToken : IDisposable
		{
			/// <summary>
			/// 环境对象。
			/// </summary>
			private AvsEnvironment env;

			/// <summary>
			/// 初始化一个新的凭据。
			/// </summary>
			/// <param name="env">新凭据对应的环境。</param>
			public ContextToken(AvsEnvironment env)
			{
				this.env = env;
				this.env.PushContext();
			}

			#region IDisposable 成员

			/// <summary>
			/// 释放当前的对象。
			/// </summary>
			public void Dispose()
			{
				this.Dispose(true);
				GC.SuppressFinalize(this);
			}

			/// <summary>
			/// 释放对象占用的资源。
			/// </summary>
			/// <param name="disposing">指定是否要释放非托管资源。</param>
			private void Dispose(bool disposing)
			{
				this.env.PopContext();
			}

			/// <summary>
			/// 析构方法。
			/// </summary>
			~ContextToken()
			{
				this.Dispose(false);
			}

			#endregion
		}

		#endregion

		#region 退出管理

		/// <summary>
		/// 退出时相关的用户数据。
		/// </summary>
		private IntPtr exitUserData;

		/// <summary>
		/// 获取或设置退出时退出消息将要接收到的用户数据。
		/// </summary>
		public IntPtr ExitUserData
		{
			get
			{
				return this.exitUserData;
			}
			set
			{
				this.exitUserData = value;
				this.CheckAndRegisterExitCallback();
			}
		}

		/// <summary>
		/// 检查是否有任何退出事件绑定到了引擎，并用指定的数据注册退出代理。
		/// </summary>
		/// <param name="userData">退出时提供给事件接收者的回调函数。</param>
		private void CheckAndRegisterExitCallback()
		{
			// 如果没有注册任何事件，则取消退出回调以增加性能。
			if (this.exitCallback != null)
			{
				Native.NativeMethods.avs_at_exit(this.ptr, this.ExitCallbackAgent, this.exitUserData);
			}
			else
			{
				Native.NativeMethods.avs_at_exit(this.ptr, null, IntPtr.Zero);
			}
		}


		/// <summary>
		/// 退出代理函数。
		/// </summary>
		/// <param name="pEnv">环境指针。</param>
		/// <param name="userData">用户数据。</param>
		private void ExitCallbackAgent(IntPtr pEnv, IntPtr userData)
		{
			this.OnExit(userData);
		}


		/// <summary>
		/// 引发 <see cref="Exit"/> 事件。
		/// </summary>
		/// <param name="userData">事件包含的用户数据。</param>
		private void OnExit(IntPtr userData)
		{
			this.OnExit(new AvsExitEventArgs(userData));
		}

		/// <summary>
		/// 引发 <see cref="Exit"/> 事件。
		/// </summary>
		/// <param name="e">事件包含的参数。</param>
		private void OnExit(AvsExitEventArgs e)
		{
			if (this.exitCallback != null)
			{
				this.exitCallback(this, e);
			}
		}

		/// <summary>
		/// 退出时回调的委托。
		/// </summary>
		private EventHandler<AvsExitEventArgs> exitCallback;

		/// <summary>
		/// 当 AVS 环境退出时引发。
		/// </summary>
		public event EventHandler<AvsExitEventArgs> Exit
		{
			add
			{
				this.exitCallback += value;
				this.CheckAndRegisterExitCallback();
			}
			remove
			{
				this.exitCallback -= value;
				this.CheckAndRegisterExitCallback();
			}
		}

		#endregion

		#endregion
	}
}
